package com.weizicheng;

import com.weizicheng.dao.InterfaceStudent;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class StudentAgency {

    public InterfaceStudent agency(){
        Student student = new Student(); //被代理对象
        /*
        给Student类生成一个代理对象并返回 :
        需要使用Proxy.newProxyInstance(); 该方法返回的就是代理对象
         */
        /*
        Proxy.newProxyInstance() 参数解释:
            ClassLoader loader :类加载器; 要求和被代理对象是相同的类加载器;
                原因 :底层使用类加载器创建代理对象的class对象.
                        有了class对象就可以使用反射技术创建代理对象给你

            Class<?>[] interfaces :接口们; 要求和目标对象实现一样的接口, 目的是让代理对象和目标(被代理)对象有相同的功能

            InvocationHandler h :该参数是一个接口类型;需要我们自己提供一个实现类对象,一般使用匿名内部类
                  作用 :在匿名内部类中处理代理对象增强的逻辑.
         */
        ClassLoader loader = student.getClass().getClassLoader(); //获得被代理对象的类加载器(参数一)

        //Class<?>[] interfaces = student.getClass().getInterfaces(); //这样写无法获取间接实现的接口, 所以不实用
        Class<?>[] interfaces = {InterfaceStudent.class}; //可以直接声明要实现的接口.(参数二)

        //参数二就是让代理对象和目标对象实现相同的接口, 所以代理对象也实现了和被代理对象相同的接口,
        //          所以这里可以将被代理对象强转为所实现的哪个接口类型(这里使用的是多态)
        InterfaceStudent proxy = (InterfaceStudent) Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {

            /**
             * 该方法就是代理对象用来做增强的逻辑方法, 外界只要代理对象的任何方法, 该方法就会执行.
             *
             * @param proxy :表示代理对象, 和Proxy.newProxyInstance()的返回值对象是同一个对象(不常用)
             * @param method :表示外界调用了代理对象的哪个方法对应的Method对象(外界调用了哪个方法; 那么method就表示被调用的这个方法).
             * @param args : 表示外界调用了代理对象方法传递的参数(可能一个参数都没有, 也可能有多个参数, 所以是数组)
             * @return
             * @throws Throwable
             * 执行被代理对象中所有方法都会经过invoke方法
             * 对method参数进行判断 :
             *      如果是要增强的方法, 那么就对其增强.
             *      如果不是, 就还是调用对象原有的功能.
             */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getName().equals("eat")){
                    //如果调用的方法是eat的方法, 那么就对它进行增强(改变).
                    System.out.println("吃什么吃.别吃了");
                    return null;
                }else{
                    //如果不是eat方法, 则调用对象原有的功能
                    return method.invoke(student,args); //调用对象原来就有的功能, 参数一是被代理的对象, 参数二是 :方法所需要的参数(可能有, 可能没有).
                }
            }
        }); //返回的是一个代理对象
        return proxy;
    }

    public InterfaceStudent agency02(){
        //1.创建被代理对象(用接口类型来接收对象; 用了多态的父new子)
        InterfaceStudent student = new Student();

        //2.通过动态代理创建代理对象
        /*
        ClassLoader loader :类加载器; 要求和被代理对象相同
        Class<?>[] interfaces :实现的接口; 要求和被代理对象相同
        InvocationHandler h :增强方法
         */
        //获得被代理对象的类加载器
        ClassLoader loader = student.getClass().getClassLoader();
        //直接声明要实现的接口
        Class<?>[] interfaces = {InterfaceStudent.class};
        //用多态将代理对象的类型设为它所实现的接口的类型
        InterfaceStudent o = (InterfaceStudent) Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                if (method.getName().equals("play")){
                    System.out.println("别玩了, 吃点?");
                    return null;
                }else{
                   return method.invoke(student,args);
                }
            }
        });
        //返回代理对象
        return o;
    }
}
